#!/bin/bash
# Run one datalog test

# If the following is set to 1 we skip the DDlog compilation
SKIP_DDLOG=0
PROFILE=0
# If flatbuf is not set, set it to 1.  If 1 we test flatbuf too
FLATBUF=${FLATBUF:-1}

set -ex

function usage {
    echo "Usage: run-test.sh testname [debug|release]"
    echo "Run one Datalog test"
    echo "The following environment variables control this script:"
    echo "- DDLOGFLAGS controls the ddlog compilation process"
    echo "- RUSTFLAGS controls the Rust compiler flags"
    echo "- RUSTFEATURES controls the Rust compilation features enabled"
    echo "- CARGOFLAGS controls the cargo (Rust package system) compilation flags"
    exit 1
}

if [ $# == 0 ]; then
    usage
fi

testname=$1
base=$(basename ${testname} .dl)
shift

build="release"
if [ $# == 1 ]; then
    build=$1
    shift
else
    usage
fi

# Always dump intermediate transformations.
# Always re-validate the program after optimization to catch potential compiler
# bugs.
DDLOGFLAGS="${DDLOGFLAGS} --pp-flat --pp-validated --pp-opt --re-validate"

if [[ -z ${RUSTFEATURES} ]]; then
    RUSTFEATURES="command-line,ovsdb"
else
    RUSTFEATURES="command-line,ovsdb,${RUSTFEATURES}"
fi

if [[ " ${DDLOGFLAGS[@]} " =~ " -g " ]]; then
    echo "Adding debugging hooks"
    DDLOGFLAGS="${DDLOGFLAGS} --pp-debug"
fi

if [ "x${FLATBUF}" == "x1" ]; then
    RUSTFEATURES="$RUSTFEATURES,flatbuf"
    DDLOGFLAGS="${DDLOGFLAGS} -j"
fi

if [ "x${build}" == "xrelease" ]; then
    CARGOFLAGS="--release ${CARGOFLAGS}"
elif [ "x${build}" == "xdebug" ]; then
    CARGOFLAGS="${CARGOFLAGS}"
else
    usage
fi

# When running in CI, the DDlog compiler should be preinstalled by the build stage.
if [ -z "${IS_CI_RUN}" -a ${SKIP_DDLOG} == "0" ]; then
    if [ "x${PROFILE}" == "x1" ]; then
        stack install --profile
        export GHCRTS="-xc"
    else
        stack install
    fi
fi

CLASSPATH=$(pwd)/${base}_ddlog/flatbuf/java:$(pwd)/../../java/ddlogapi.jar:$CLASSPATH
# Run DDlog compiler
if [ ${SKIP_DDLOG} == "0" ]; then
   ddlog -i ${base}.dl -L../../lib ${DDLOGFLAGS}

   # Validate intermediate representations.
   if [ -f ${base}.flat.ast.expected ]; then
       diff -q ${base}.flat.ast ${base}.flat.ast.expected
   fi

   if [ -f ${base}.valid.ast.expected ]; then
       diff -q ${base}.valid.ast ${base}.valid.ast.expected
   fi

   if [[ " ${DDLOGFLAGS[@]} " =~ " -g " ]]; then
       if [ -f ${base}.debug.ast.expected ]; then
           diff -q ${base}.debug.ast ${base}.debug.ast.expected
       fi
   fi

   if [ -f ${base}.opt.ast.expected ]; then
       diff -q ${base}.opt.ast ${base}.opt.ast.expected
   fi
fi

# Compile produced Rust files
cd ${base}_ddlog
cargo build ${CARGOFLAGS} --features ${RUSTFEATURES}
cd ..

if [ "x${FLATBUF}" == "x1" ]; then
    # Build the Java library with the DDlog API
    make -C ../../java
    # Build the generated Java classes for serialization (generated by flatbuf)
    (cd ${base}_ddlog/flatbuf/java && javac $(ls ddlog/__${base}/*.java) && javac $(ls ddlog/${base}/*.java))
fi

if [ -f ${base}.dat ]; then
    # Run script with input data
    (set -x; /usr/bin/time ${base}_ddlog/target/${build}/${base}_cli < ${base}.dat > ${base}.dump)
    # Compare outputs
    if [ -f ${base}.dump.expected.gz ]; then
        zdiff -q ${base}.dump ${base}.dump.expected.gz
    elif [ -f ${base}.dump.expected ]; then
        diff -q ${base}.dump ${base}.dump.expected
    fi

    # Don't dump potentially huge contents of the log to the console.
    set +x

    if [ -f ${base}.log.expected ]; then
        diff -q <(echo `sort ../${base}.log`) <(echo `sort ${base}.log.expected`)
    fi
fi
# Remove outputs
rm -rf ${base}.dump ${base}.dump.gz ../${base}.log
# Additional cleanup possible
# rm -rf ${base}_ddlog
